package org.xxkk.utils.bo;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.xxkk.utils.Main;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author xxkk
 * @version 1.0.0
 * @title ExcelParser
 * @description ʕ•ᴥ•ʔ
 * @create 2023/12/26 10:01
 **/
public class ExcelParser {
    //    public String outputPath = "/excel2XJT_output/";  // 设置输出主目录
    public String outputXMLPath = "/Xml/";  // 设置输出子目录
    public String outputJSONPath = "/Json/";
    public String outputTSPath = "/Ts/";
    public String outputTSdPath = "/Tsd/";
    public String outputI18nPath = "/I18n/";// cocos i18n 文件输出相对路径
    public static final String format = "UTF-8";// 输出文件内容编码形式
    private String absPath;
    public String parseType = null;// 转换文件 2 xml,json,ts,tsd
    private BufferedWriter bufferedWriter;// 在输出文件.d.ts使用
    private OutputStreamWriter outputStreamWriter;// 不用理会
    private Map<String, BufferedWriter> i18nBufferWriterMap = new ConcurrentHashMap<>();// 一种语言一个输入包装类 TODO 这里有多种i18配置文件输出，需要多个输出流
    public static ExecutorService executor = Executors.newCachedThreadPool();// 开启多线程并发执行
    public static int shutDownFlag = 0;// 文件转换结束关闭线程池使用
    //i18n组件开头代码
    public static String i18nHeader = "const win = window as any;\n" +
            "\n" +
            "export const languages = {\n";
    // i18n组件末尾代码
    public static String i18nEnd = "};\n" +
            "\n" +
            "if (!win.languages) {\n" +
            "    win.languages = {};\n" +
            "}\n" +
            "\n" +
            "win.languages.";//zh+" = languages;";

    /**
     * 之前的excel转换文件遇到文件过多时转换太慢了，
     * 思考后对项目做出了调整，
     * 在转换时创建新的线程池使用多线程的方式并发执行转换并且在文件转换完成后100毫秒后关闭线程池。
     * 另外再遇到子目录时采用递归的方式重新调用
     *
     * @param directory excel文件所在主目录
     */
    public void parse(File directory) throws IOException {
        File[] files = directory.listFiles();
        if (null != files && 0 < files.length) {
            ExcelParser.shutDownFlag += files.length;// 计数 关闭executor
            for (File file : files) {
                if (file.isDirectory()) {// 如果存在子目录则递归
                    this.parse(new File(file.getCanonicalPath()));
                    shutDownFlag--;
                } else {
                    if (file.toString().endsWith(".svn") || file.toString().startsWith("~$")) {
                        System.out.println("过滤无效文件：" + file.getName());
                        shutDownFlag--;
                        continue;
                    }
                    ExcelSheetInfo sheetInfo = this.getExcelSheetInfo(file); // 初始化excel sheetinfo
                    if (sheetInfo == null) {
                        System.out.println("过滤非excel文件：" + file.getName());
                        shutDownFlag--;
                        continue;
                    }
                    executor.execute(() -> {
                        try {
                            this.parse0(sheetInfo);
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            shutDownFlag--;
                            close();
                        }
                    });
                }
            }
        } else {
            System.out.println("异常问题：" + directory.getName() + "不存在文件 当前程序查找路径为：" + directory.getCanonicalPath()
                    + "\n Tips:如果需要更改路径，请在命令行中添加第一个参数为指定的路径");
        }
        close();
    }

    public void close() {
        try {
            if (shutDownFlag <= 0) {
                executor.shutdown();// 线程执行结束 关闭线程池
                executor.awaitTermination(512, TimeUnit.MILLISECONDS);
                if (!executor.isShutdown()) {
                    executor.awaitTermination(1200, TimeUnit.MILLISECONDS);
                    executor.shutdown();
                    System.out.println("警告：线程超时关闭");
                }
                for (String k : this.i18nBufferWriterMap.keySet()) {
                    BufferedWriter writer = this.i18nBufferWriterMap.get(k);
                    writer.write(i18nEnd + k + " = languages;");// 结束写入i18n组件末尾代码，一种语言一个文件输入流
                    writer.flush();
                    writer.close();
                }
                System.out.println("总耗时：" + (new Date().getTime() - Main.date_start.getTime()) / 1000.0 + "秒");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void parse0(ExcelSheetInfo sheetInfo) {
        System.out.println(sheetInfo.getFileName());
        if (this.parseType == null) {
            this.parse2xml(sheetInfo);
            this.parse2json(sheetInfo);
            this.parse2ts(sheetInfo);
            this.parse2tsd(sheetInfo);
            this.parse2i18n(sheetInfo);
        } else {
            if (this.parseType.contains("xml"))
                this.parse2xml(sheetInfo);
            if (this.parseType.contains("json"))
                this.parse2json(sheetInfo);
            if (this.parseType.contains("ts"))
                this.parse2ts(sheetInfo);
            if (this.parseType.contains("tsd"))
                this.parse2tsd(sheetInfo);
            if (this.parseType.contains("i18n"))
                this.parse2i18n(sheetInfo);
        }
        sheetInfo.close();
    }

    private void parse2i18n(ExcelSheetInfo sheetInfo) {
        sheetInfo.parseI18n(this);// this互调 为了获取输出流包装类
    }

    // 2xml
    private void parse2xml(ExcelSheetInfo sheetInfo) {
        // xml文件输出路径
        String outputPathFile = this.getXmlOutputDir() + sheetInfo.getOutput_file_name() + ".xml";
        sheetInfo.parseXML(outputPathFile);
    }

    private ExcelSheetInfo getExcelSheetInfo(File file) {
        Workbook workbook = this.getWorkbook(file);
        if (workbook == null)
            return null;
        Sheet sheet = workbook.getSheetAt(0);
        ExcelSheetInfo excelSheetInfo = new ExcelSheetInfo(file.getName(), sheet, workbook, this);
        return excelSheetInfo;
    }

    private Workbook getWorkbook(File file) {
        Workbook workbook = null;
        try {
            String filePath = file.getCanonicalPath();
//            System.out.println("2:" + filePath);
            InputStream inputStream = Files.newInputStream(Paths.get(filePath));
            if (filePath.endsWith(".xlsx")) {
                workbook = new XSSFWorkbook(inputStream);
            } else if (filePath.endsWith(".xls")) {
                workbook = new HSSFWorkbook(inputStream);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
//        System.out.println("3:" + workbook.toString());
        return workbook;
    }

    // 2json
    public void parse2json(ExcelSheetInfo sheetInfo) {
        String jsonPathFile = this.getJsonOutputDir() + sheetInfo.getOutput_file_name() + ".json";
        sheetInfo.parseJson(jsonPathFile);
//        System.out.println("json输出路径 " + jsonPathFile);
    }

    // 2ts
    public void parse2ts(ExcelSheetInfo sheetInfo) {
        String tsPathFile = this.getTsOutputDir() + sheetInfo.getOutput_file_name() + ".ts";
        sheetInfo.parseTs(tsPathFile);
    }

    public String getTsDOutputDir() {// .d.ts文件输出主目录
        return this.absPath + this.outputTSdPath;
    }

    public String getTsOutputDir() {
        return this.absPath + this.outputTSPath;
    }

    public String getXmlOutputDir() {
        return this.absPath + this.outputXMLPath;//xml输出主目录
    }

    public String getJsonOutputDir() {
        return this.absPath + this.outputJSONPath;
    }

    public void parse2tsd(ExcelSheetInfo sheetInfo) {
        sheetInfo.parseDTs(this.bufferedWriter);
    }

    public void setAbsPath(String absPath) {
        this.absPath = absPath;
    }

    public void setBufferedWriter(BufferedWriter bufferedWriter) {
        this.bufferedWriter = bufferedWriter;
    }

    public void setOutputStreamWriter(OutputStreamWriter outputStreamWriter) {
        this.outputStreamWriter = outputStreamWriter;
    }

    public String getI18nOutputDir() {
        return this.absPath + this.outputI18nPath;
    }

    /**
     * 获取i18n文件输出流，若没有则创建输出流 考虑了存在并发 线程安全
     *
     * @param fileName 译文名 excel表格的name行的数据 例如：zh en ...
     * @return
     */
    public BufferedWriter getI18nWriter(String fileName) {
        try {
            BufferedWriter writer = this.i18nBufferWriterMap.get(fileName);
            if (writer != null)
                return writer;
            // 创建io
            String pathAndFilename = this.getI18nOutputDir() + fileName + ".ts";
            File file = new File(pathAndFilename);
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file), ExcelParser.format);
            writer = new BufferedWriter(outputStreamWriter);
            BufferedWriter writer2 = this.i18nBufferWriterMap.putIfAbsent(fileName, writer);
            writer = writer2 == null ? writer : writer2;
            writer.write(i18nHeader);
            writer.flush();
            return writer;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}
